﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net.Mail;
using System.Net.Mime;
using System.IO;
using System.Text.RegularExpressions;
using System.Xml.Linq;

namespace SMTPTestSuite
{
    public partial class FormSendBulk : Form
    {
        private static Random _Random = new Random();
        private static int _RecipientIndex = 0;
        private ListBox oLogListBox;

        public FormSendBulk(ListBox LogListBox)
        {
            InitializeComponent();
            UpdateRecipientButtons();
            UpdateProcessInlineImagesUI();
            oLogListBox = LogListBox;
        }

        ~FormSendBulk()
        {

        }

        private void Log(string LogData)
        {
            if (oLogListBox.InvokeRequired)
            {
                oLogListBox.Invoke(new MethodInvoker(delegate() { oLogListBox.Items.Add(LogData); }));
            }
            else
                oLogListBox.Items.Add(LogData);
        }

        private void UpdateRecipientButtons()
        {
            if (String.IsNullOrEmpty(textBoxAddRecipient.Text))
            {
                buttonAddRecipient.Enabled = false;
            }
            else
            {
                string sAddRecipient=textBoxAddRecipient.Text;
                if (listBoxRecipients.Items.IndexOf(sAddRecipient)>=0)
                {
                    buttonAddRecipient.Enabled=false;
                }
                else
                    buttonAddRecipient.Enabled=true;
            }
            buttonRemoveRecipient.Enabled = (listBoxRecipients.SelectedIndex >= 0);
        }

        private void UpdateSenderOptions()
        {
            if (radioButtonSpecificSender.Checked)
            {
            }
        }

        private void buttonAddRecipient_Click(object sender, EventArgs e)
        {
            listBoxRecipients.Items.Add(textBoxAddRecipient.Text);
            UpdateRecipientButtons();
        }

        private void textBoxAddRecipient_TextChanged(object sender, EventArgs e)
        {
            UpdateRecipientButtons();
        }

        private void buttonClose_Click(object sender, EventArgs e)
        {
            this.Dispose();
        }

        private void listBoxRecipients_SelectedIndexChanged(object sender, EventArgs e)
        {
            UpdateRecipientButtons();
        }

        private void buttonStart_Click(object sender, EventArgs e)
        {
            // Validate the run parameters.  If invalid, we'll send one message
            if (numericUpDownMessageNum.Value >= numericUpDownStopMessageNumber.Value)
                numericUpDownStopMessageNumber.Value = numericUpDownMessageNum.Value+1;

            // Check if user has forgotten to add recipient to send list (I do this constantly!)
            if (listBoxRecipients.Items.Count==0 && !String.IsNullOrEmpty(textBoxAddRecipient.Text))
            {
                buttonAddRecipient_Click(null, null);
            }

            timerSend.Interval = (int)(numericUpDownSendDelay.Value * 1000);
            timerSend.Start();
            buttonStart.Enabled = false;
            buttonStop.Enabled = true;
        }

        private void buttonStop_Click(object sender, EventArgs e)
        {
            timerSend.Stop();
            buttonStart.Enabled = true;
            buttonStop.Enabled = false;
        }

        private string Subject()
        {
            return textBoxSubject.Text.Replace("%N", numericUpDownMessageNum.Value.ToString());
        }

        private string Sender()
        {
            if (radioButtonSpecificSender.Checked)
                return textBoxSpecificSender.Text;

            // Generate random sender email based on criteria specified
            string sDomain = textBoxSenderDomainTemplate.Text;
            string sUser = textBoxSenderUserTemplate.Text;
            return ReplaceWildcards(sUser) + "@" + ReplaceWildcards(sDomain);
        }

        private string ReplaceWildcards(String WildcardString)
        {
            // Replace the wildcards found in the string with random letters
            while (WildcardString.Contains("*"))
            {
                int i = WildcardString.IndexOf("*");
                if (i == 0)
                {
                    WildcardString = WildcardAsterisk() + WildcardString.Substring(i + 1);
                }
                else
                {
                    WildcardString = WildcardString.Substring(0, i) + WildcardAsterisk() + WildcardString.Substring(i + 1);
                }
            }
            return WildcardString;
        }

        private string WildcardAsterisk()
        {
            // Return a random string
            const string sSourceChars = "abcdefghijklmnopqrstuvwxyz";
            int iStringLength = _Random.Next(8)+3;
            string sWildString = "";
            for (int i = 0; i < iStringLength; i++)
            {
                sWildString += sSourceChars[_Random.Next(sSourceChars.Length)];
            }
            return sWildString;
        }

        private void timerSend_Tick(object sender, EventArgs e)
        {
            // Send a message
            timerSend.Stop();
            SendMessage();
            if (numericUpDownMessageNum.Value >= numericUpDownStopMessageNumber.Value)
            {
                // We've sent enough messages, so stop
                buttonStop_Click(this, null);
            }
            else
                timerSend.Start();
        }

        private void SendMessage()
        {
            // Check we have recipients
            if (listBoxRecipients.Items.Count < 1)
            {
                Log("No recipients specified, cannot send message");
                return;
            }
            // Build the message
            MailMessage oMessage = new MailMessage();
            oMessage.From = new MailAddress(this.Sender());
            if (radioButtonSendToEachRecipient.Checked)
            {
                // Send message to each recipient as we loop
                Log("Sending " + this.Subject() + " to " + listBoxRecipients.Items[_RecipientIndex].ToString());
                oMessage.To.Add(new MailAddress(listBoxRecipients.Items[_RecipientIndex].ToString()));
                _RecipientIndex++;
                if (_RecipientIndex > listBoxRecipients.Items.Count-1)
                    _RecipientIndex = 0;
            }
            else
            {
                // Send message to all recipients
                for (int i = 0; i < listBoxRecipients.Items.Count; i++)
                    oMessage.To.Add(new MailAddress(listBoxRecipients.Items[i].ToString()));
            }
            oMessage.Subject = this.Subject();

            oMessage.Body = textBoxBody.Text;
            if (checkBoxProcessInlineImages.Checked)
            {
                ProcessInlineImages(oMessage);
            }

            if (checkBoxAddAttachment.Checked)
            {
                // Add an attachment
                int iAttachSize = (int)numericUpDownAttachSize.Value;
                if (comboBoxAttachSizeUnit.SelectedIndex>0)
                    iAttachSize = iAttachSize * (int)(System.Math.Pow((double)1024, (double)comboBoxAttachSizeUnit.SelectedIndex));

                byte[] bytAttach=new byte[iAttachSize];
                MemoryStream oAttachMS = new MemoryStream(bytAttach);

                Attachment oAttach = new Attachment(oAttachMS, "Attachment.txt", "text/plain");
                oMessage.Attachments.Add(oAttach);
            }

            SendMessage(textBoxTargetSMTPServer.Text, (int)numericUpDownSMTPPort.Value, oMessage);
            if (checkBoxSendToLocalSmtp.Checked)
                SendMessage("localhost", 25, oMessage);

            oMessage.Dispose();

            numericUpDownMessageNum.Value++;
        }

        private void SendMessage(string SMTPServer, int SMTPPort, MailMessage mailMessage)
        {
            try
            {
                SmtpClient oClient = new SmtpClient(SMTPServer, SMTPPort);
                oClient.Send(mailMessage);
                oClient.PickupDirectoryLocation = "C:\\Temp\\Send";
                oClient.DeliveryMethod = SmtpDeliveryMethod.SpecifiedPickupDirectory;
                oClient.Send(mailMessage);
            }
            catch (Exception ex)
            {
                string sError = "";
                if (ex is SmtpException)
                {
                    sError = (ex as SmtpException).StatusCode.ToString() + " " + ex.Message;
                }
                else
                {
                    sError = ex.Message;
                }
                System.Windows.Forms.MessageBox.Show(sError, "Failed to send message", MessageBoxButtons.OK, MessageBoxIcon.Warning);
            }
        }

        internal IEnumerable<String> getImageFromTemplate(ref string templateContent, bool replaceTagImageInTemplate)
        {
            Regex regex = new Regex(@"<img\s[^>]*?src\s*=\s*['\""]([^'\""]*?)['\""][^>]*?>");
            MatchCollection matches = regex.Matches(templateContent);
            List<String> result = new List<String>();

            foreach (Match match in matches)
            {
                XDocument doc = XDocument.Parse(match.Value);

                string value = doc.Root.Attribute("src").Value;

                if (replaceTagImageInTemplate)
                {
                    if (value.ToLower().Length < 4 || (value.ToLower().Length >= 4 && value.ToLower().Substring(0, 4) != "cid:"))
                    {
                        templateContent = templateContent.Replace("src=\"" + value + "\"", "src=\"cid:" + value + "\"");
                    }
                }

                if (!result.Contains(value))
                    result.Add(value);
            }

            return result;
        }
        private void ProcessInlineImages(MailMessage oMessage)
        {
            string sBody = oMessage.Body;
            IEnumerable<String> images = getImageFromTemplate(ref sBody, true);

            AlternateView viewHtml = AlternateView.CreateAlternateViewFromString(sBody, System.Text.Encoding.UTF8, "text/html");

            foreach (string imageToAttach in images)
            {
                if (!string.IsNullOrEmpty(imageToAttach))
                {

                    FileInfo fileInfo = new FileInfo(String.Format("{0}\\{1}", textBoxImageFolder.Text, imageToAttach));
                    if (fileInfo != null)
                    {
                        string sContentType = String.Format("image/{0}", fileInfo.Extension.Substring(1));
                        FileStream stream = fileInfo.OpenRead();
                        // Add with Content-Disposition
                        Attachment attachment = new Attachment(stream, sContentType);
                        oMessage.Attachments.Add(attachment);
                        if (checkBoxIncludeContentDisposition.Checked)
                        {
                            attachment.ContentDisposition.Inline = true;
                            attachment.ContentDisposition.FileName = imageToAttach;
                        }
                        attachment.ContentId = imageToAttach;
                    }
                }
            }

            oMessage.AlternateViews.Add(viewHtml);
        }

        private void buttonRemoveRecipient_Click(object sender, EventArgs e)
        {
            while (listBoxRecipients.SelectedIndices.Count>0)
            {
                listBoxRecipients.Items.RemoveAt(listBoxRecipients.SelectedIndices[listBoxRecipients.SelectedIndices.Count - 1]);
            }
        }

        private void textBoxBody_TextChanged(object sender, EventArgs e)
        {

        }

        private void buttonLoadBodyFromFile_Click(object sender, EventArgs e)
        {
            OpenFileDialog oDialog = new OpenFileDialog();
            oDialog.Filter = "HTML files (*.htm,*.html)|*.htm;*.html|Text files (*.txt)|*.txt|All Files|*.*";
            oDialog.DefaultExt = "txt";
            oDialog.Title = "Select body source file";
            oDialog.CheckFileExists = true;
            if (oDialog.ShowDialog() != DialogResult.OK)
                return;

            try
            {
                using (StreamReader streamReader = new StreamReader(oDialog.FileName))
                {
                    textBoxBody.Text = streamReader.ReadToEnd();
                }
            }
            catch (Exception ex)
            {
                System.Windows.Forms.MessageBox.Show(this, "Failed to read file contents: " + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void buttonChooseImageFolder_Click(object sender, EventArgs e)
        {
            FolderBrowserDialog oDialog = new FolderBrowserDialog();
            if (oDialog.ShowDialog() != DialogResult.OK)
                return;
            textBoxImageFolder.Text = oDialog.SelectedPath;
        }

        private void UpdateProcessInlineImagesUI()
        {
            bool enabled = checkBoxProcessInlineImages.Checked;
            textBoxImageFolder.Enabled=enabled;
            buttonChooseImageFolder.Enabled=enabled;
            checkBoxIncludeContentDisposition.Enabled = enabled;
        }

        private void checkBoxProcessInlineImages_CheckedChanged(object sender, EventArgs e)
        {
            UpdateProcessInlineImagesUI();
        }
    }
}
